#include "lib.h"

#pragma warning( disable: 4786 )
#include <vector>

#include "slotconn.h"
#include "sltsql.h"
#include "slotguts.h"
#include "getrand.h"
#include "opts.h"
#include "log.h"

#include "llguts.h"               //   0
#include "diamguts.h"             //   1
#include "dddguts.h"              //   2
#include "rwbguts.h"              //   3
#include "bl7guts.h"              //   4
#include "bonkguts.h"             //   5
#include "boiguts.h"              //   6
#include "gbadguts.h"             //   7
#include "crazydragonguts.h"      //   8
#include "heistguts.h"            //   9
#include "lightspeedguts.h"       //   10
#include "myowguts.h"			  //   11
#include "frozenguts.h"			  //   12
#include "dianeguts.h"			  //   13
#include "eltoroguts.h"			  //   14
#include "pharguts.h"		   	  //   16 or 0x10
#include "hrollerguts.h"		  //   17 or 0x11

//#include "hikeguts.h"		      //  80 (0x50) (Commented out per Mikey 2/28)
#include "ffguts.h"			      // 160 (0xA0)
#include "csguts.h"			      // 161 (0xA1)
#include "ssguts.h"			      // 162 (0xA2)
#include "midlifeguts.h"		  // 163 (0xA3)
#include "jazzguts.h"			  // 164 (0xA4)
#include "superdiamondmineguts.h" // 165 (0xA5)
#include "monsterguts.h"		  // 166 (0xA6)

SlotGuts *
SlotGuts::createGuts(uint8 machid)
{
    // Defect 1990 - GChapman - 10/14/2003
    // Per Mike, I have added a comment here.
    // We recently removed a check in pub_SlotCompleteGame
    // to ensure if the gut id changes when restoring a
    // a game that there is no problem.  This only works
    // if bonus games are not gut dependent. Currently this
    // is true.  If that changes, the stored procdure mentioned
    // above should be revisited.
    SlotGuts *guts = NULL;
    switch ( machid )
    {
        case 0:
        case 0xf0:
            guts = new LLSlotGuts();
            break;

        case 1:
        case 0xf1:
            guts = new DiamSlotGuts();
            break;

        case 2:
        case 0xf2:
            guts = new DDDSlotGuts();
            break;

        case 3:
        case 0xf3:
            guts = new RWBSlotGuts();
            break;

        case 4:
        case 0xf4:
            guts = new BL7SlotGuts();
            break;

        case 5:
            guts = new BonkSlotGuts();
            break;

        case 6:
            guts = new BOISlotGuts();
            break;

        case 7:
            guts = new GoodBadSlotGuts();
            break;

        case 8:
            guts = new CrazyDragonSlotGuts();
            break;

        case 9:
            guts = new HeistSlotGuts();
            break;

        case 10:
            guts = new LightSpeedSlotGuts();
            break;

		case 11:
			guts = new MyowSlotGuts();
			break;

		case 12:
			guts = new FrozenSlotGuts();
			break;

		case 13:
			guts = new DianeSlotGuts();
			break;

		case 14:
			guts = new ElToroSlotGuts();
			break;

		case 0x10:
			guts = new PharSlotGuts();
			break;

		case 0x11:
			guts = new HRollerSlotGuts();
			break;


//		Commented out per Mikey 2/28
//		case 0x50:
//			guts = new HikeSlotGuts();
//			break;

		case 0xA0:
			guts = new FoodFightSlotGuts();
			break;

		case 0xA1:
			guts = new CardSharkSlotGuts();
			break;

		case 0xA2:
			guts = new ShoppingSpreeSlotGuts();
			break;

		case 0xA3:
			guts = new MidLifeCrisisSlotGuts();
			break;

		case 0xA4:
			guts = new JazzTimeSlotGuts();
			break;

        case 0xA5:
            guts = new SuperDiamondMineGuts();
            break;

//        case 0xA6:
//            guts = new MonsterSlotGuts();
//            break;

        default:
            ASSERT(false);
            break;

    }

    if ( guts )
    {
        guts->m_machid = machid;
    }

    return guts;
}



SlotGuts::SlotGuts()
{
    m_hardMalfunction = false;
	m_info = (Info *) 0;
    m_referenceGameNum = 0;
	setGameComplete(false);
	m_hasSavedGame = false;
	m_subGame = 0; 
    m_bFastPlayEnabled = false;
    m_bCanResetForNewGame = true;

#ifdef DEBUGSPIN
	m_bDebugSpinsMode = ServerOpts::bAllowTesting;

	if(m_bDebugSpinsMode)
	{
		Log::msgInformation("Debug Spins Mode is active");
	}
#endif
}

SlotGuts::~SlotGuts()
{
    delete m_subGame;
    clearSpins();
    clearWheelhouse();
    clearPayTable();
    clearPaylines();
    clearDenominations();
}
    
void
SlotGuts::clearPaylines()
{
    while ( m_paylines.size() > 0 )
    {
        SlotPaylineReel *paylinereel = m_paylines.back();
        
        while ( paylinereel->size() > 0 )
        {
            SlotPaylinePosition *position = paylinereel->back();
            delete position;
            paylinereel->pop_back();
        }
        delete paylinereel;
        m_paylines.pop_back();
    }
}


		
void *
SlotGuts::getCustomData()
{
    ASSERT(0);
    return (void *) 0;
}

void 
SlotGuts::resetForNewGame()
{
    if ( getCanResetForNewGame() )
    {
	    setGameComplete(false);
        m_referenceGameNum = 0;
        clearSpins();
    }
}

void 
SlotGuts::setCanResetForNewGame(bool8 bCanResetForNewGame)
{
    m_bCanResetForNewGame = bCanResetForNewGame;
}

bool8 
SlotGuts::getCanResetForNewGame() const
{
    return m_bCanResetForNewGame;
}

void 
SlotGuts::clearSpins()
{
    while ( m_spins.size() > 0 )
    {
        SlotSpin *spin = m_spins.back();
        delete spin;
        m_spins.pop_back();
    }
}
		
int32 
SlotGuts::getReferenceGameNum()
{
    return m_referenceGameNum;
}


int32 
SlotGuts::getGutsId()
{
    return m_gutsid;
}

void
SlotGuts::getGutsName(char* name)
{
   SlotsSQL *slotTracker = (SlotsSQL *) tracker;

    if ( tracker )
    {
    	slotTracker->lock();
        slotTracker->getGutsName(m_gutsid,name);
    	slotTracker->unlock();
    }
}

void 
SlotGuts::setGutsId(int32 gutsId)
{
    m_gutsid = gutsId;

#ifdef DEBUGSPIN
	if(m_bDebugSpinsMode && shouldSaveGameData())
	{
		m_nDebugSpinIndex = 0;

		SlotsSQL *slotTracker = (SlotsSQL *) tracker;
		if ( tracker )
		{
			slotTracker->lock();
			slotTracker->getDebugSpins(m_gutsid, m_debugReelStops, m_debugSpinCount,getPid());
			slotTracker->unlock();
		}

		initDebugFunctionMap();
	}
#endif
}

		
const PID&
SlotGuts::getPid()
{
    return m_info->pid;
}



bool8 
SlotGuts::isForMoney()
{
    return m_info->forMoney;
}

bool8
SlotGuts::shouldSaveGameData()
{
	if ( isForMoney() )
	{
		return true;
	}

	if ( ServerOpts::bSaveFunData )
	{
		return true;
	}

	return false;
}

int32 
SlotGuts::getJackpotId()
{
    return m_jackpotid;
}

void 
SlotGuts::setJackpotId(int32 jackpotId)
{
    m_jackpotid = jackpotId;
}


bool8
SlotGuts::isProgressive()
{
    return m_progressive;
}

void 
SlotGuts::setProgressive(bool8 progressive)
{
    m_progressive = progressive;
}

uint8 
SlotGuts::getMachId()
{
    return m_machid;
}

void
SlotGuts::loadSubGameData()
{
    SlotsSQL *slotTracker = (SlotsSQL *) tracker;

    if ( tracker )
    {
		//if we are not saving fun play game data, do not attempt to load
		//fun play game data
		if(shouldSaveGameData())
		{
    		slotTracker->lock();
			slotTracker->getSubGameData(m_subGame);
    		slotTracker->unlock();
		}
    }

}


void
SlotGuts::saveSubGameData()
{
	if(shouldSaveGameData())
	{
		if ( getSubGameSize() > 0 )
		{
			SlotsSQL *slotTracker = (SlotsSQL *) tracker;

			if ( tracker )
			{
    			slotTracker->lock();
				slotTracker->setSubGameData(m_subGame);
    			slotTracker->unlock();
			}
		}
	}
}

// Call this method after game is saved, otherwise you cannot get a valid gamenum
// SlotGuts::saveGame() should have been called before you call this method.
void
SlotGuts::archiveSubGameData(int32 gamenum)
{
    if ( getSubGameSize() > 0 && gamenum > 0)
    {
        SlotsSQL *slotTracker = (SlotsSQL *) tracker;

        if ( tracker )
        {
    		slotTracker->lock();
            slotTracker->archiveSubGameData(gamenum, m_subGame);
    		slotTracker->unlock();
        }
    }
}

void
SlotGuts::allocateSubGameData()
{
    m_subGame = new SubGame(m_info->machineID, m_info->pid, m_info->machineSubID, m_info->forMoney);
}

int32 
SlotGuts::getSubGameSize()
{
    return m_subGame->getDataCount();
}

        
SubGame *
SlotGuts::getSubGame()
{
    return m_subGame;
}

uint8
SlotGuts::getSlotSpinCount()
{
    return m_spins.size();
}


SlotSpin *
SlotGuts::getSlotSpin(uint8 spinid)
{
    SlotSpin *spin = (SlotSpin *) 0;

    if ( spinid < m_spins.size() )
    {
        spin = m_spins[spinid];
    }
    else
    {
        /**
         * spinid is too large.
         */
			Log::msgWarning("Trying to play a bonus game, but the game is gone");

 //       ASSERT(0);
    }

    return spin; 
}


bool8
SlotGuts::isValidBet(uint8 bet)
{
    bool8 rc;

    if ( bet > 0 && bet <= m_payTable.size() )
    {
        rc = true;
    }
    else
    {
        rc = false;
    }

    return rc;
}

void 
SlotGuts::playBonusGame(uint8 bonusGameType, int32 bonusDataCount, int32 *bonusData)
{
    /* empty for subclass behavior */
}


        
void 
SlotGuts::makeWinningSpin(SlotSpin *spin)
{
    uint8 *stops = new uint8[getReelCount()];

    /**
     * Spin until we get a winner.
     */
    while ((spin->getTotalPayoutAmount() == 0 ) && (!spin->getJackpot()))
    {
			for ( uint8 i = 0; i < getReelCount() ; i++)
			{
				stops[i] = this->findPositionByWeight(getrand(), i);
			}
			spin->setStops(getReelCount(), stops);
			classifyResult(spin);
		
    }

    spin->setSpinComplete(true);
    m_spins.push_back(spin);

#ifdef DEBUGVERBOSE
	DEBUGMSG(("MakeWinningSpin - Spin: bet(%d) payout(%d) jackpot(%d)",
              spin->getTotalBet(), spin->getTotalPayoutAmount() / m_info->creditValue, spin->getJackpot()));
    for ( uint8 reelid = 0; reelid < getReelCount(); reelid++ )
    {
        DEBUGMSG(("                     wheel %d: [%s])",
                  reelid, 
                  getIcon(reelid, spin->getStops()[reelid])->iconName));
    }
#endif

    delete [] stops;

}

SlotSpin *
SlotGuts::makeWinningSpin(uint8 bet, uint8 bonusGame)
{
    SlotSpin *spin = new SlotSpin();

    spin->setBet(bet);
    spin->setBonusGameType(bonusGame);

    makeWinningSpin(spin);

    return spin;
}

SlotSpin * 
SlotGuts::spin(uint8 bet, int32 multibankBetCents, uint8 *lines, uint8 lineCount, uint8 bonusGame)
{
    SlotSpin *spin = new SlotSpin();
    
    for ( uint8 i = 0; i < lineCount; i++ )
    {
        spin->addLine(lines[i]);
    }

    spin->setBet(bet);
    spin->setMultibankBet(multibankBetCents);
    spin->setBonusGameType(bonusGame);

    this->spin(spin);

    return spin;
}


		
SlotSpin * 
SlotGuts::spin(uint8 bet, uint8 *lines, uint8 lineCount, uint8 bonusGame)
{
    return spin(bet, 0, lines, lineCount, bonusGame);
}



// kharmon.  08-17-05.  #4962.  #5116.  Check to make sure that we got valid data
// in the spin request message from the client.
bool SlotGuts::isValidSpin(SlotSpin *spin)
{
	bool bRetVal = true;

	int32 nLineCount = spin->getSpinLineCount();
	int32 nStopCount = spin->getStopCount();

	for(int32 nI=0; bRetVal==true && nI<nLineCount; nI++)
	{
		const SpinLine *spinLine = spin->getSpinLineAt(nI);

		for(int32 nJ=0; nJ<nStopCount; nJ++)
		{
			// the main suspect in the data we're given is that there
			// might be invalid lineids, so make sure that none of
			// these return a NULL.  If so, then the spin isn't valid.
			if(getPayline(spinLine->lineid, nJ)==NULL)
			{
                Log::msgError("isSpinValid:  Invalid spin parameters detected.");
				bRetVal = false;
				break;
			}
		}
	}

	return bRetVal;
}



void
SlotGuts::spin(SlotSpin *spin)
{
    uint8 *stops = new uint8[getReelCount()];
    uint8 i;

	for ( i = 0; i < getReelCount(); i++)
    {
		stops[i] = findPositionByWeight(getrand(), i);
    }

    spin->setStops(getReelCount(), stops);

    // kharmon.  08-17-05.  #4962.  #5116.  Check to make sure that we got valid data
    // in the spin request message from the client.
    m_hardMalfunction = !isValidSpin(spin);

	if(m_hardMalfunction==false)
	{
		// After the reels have stopped, before they are evaluated,
		// allow the derived slot machine to take some action.
		preSpinClassifyResults( spin );

		classifyResult(spin);

		// After the reels have been evaluated, again allow
		// the derived slot machine to take some action.
		postSpinClassifyResults( spin );

		spin->setSpinComplete(true);

		m_spins.push_back(spin);

#ifdef DEBUGVERBOSE

		DEBUGMSG(("SlotGuts.cpp - Spin: bet(%d) payout(%d) jackpot(%d)",
				  spin->getTotalBet(), spin->getTotalPayoutAmount() / m_info->creditValue, spin->getJackpot()));

		for ( uint8 reelid = 0; reelid < getReelCount(); reelid++ )
		{
			DEBUGMSG(("                     wheel %d: [%s])",
					  reelid, 
					  getIcon(reelid, spin->getStops()[reelid])->iconName));
		}
#endif
	}

    delete [] stops;
	setGameComplete(true);
}


uint8
SlotGuts::relativeWheelPosition(uint8 reelid, uint8 position, uint8 offset)
{
    uint8 newPosition = position + offset;

    if ( newPosition >= getIconCount(reelid) )
    {
        newPosition -= this->getIconCount(reelid);
    }

    return newPosition;
}

void
SlotGuts::classifyResult(SlotSpin *spin)
{
	uint8 *classifyPositions = new uint8[spin->getStopCount()];
    uint8 bet;
    uint8 result = 0;

    /**
     * Go through the played paylines one at a time looking for winners.
     */
    for ( uint8 lineidx = 0; lineidx < spin->getSpinLineCount(); lineidx++ )
    {
        const SpinLine *spinLine = spin->getSpinLineAt(lineidx);

        /**
         * Iterate the stops to get the classify positions.
         */
        for ( uint8 stopid = 0; stopid < spin->getStopCount(); stopid++ )
        {
            uint8 offsetPosition = getPayline(spinLine->lineid, stopid)->wheelposition;
            /**
             * Calculate the offset so we are using the proper icon
             * for this payline.
             */
            classifyPositions[stopid] = relativeWheelPosition(stopid, spin->getStops()[stopid], offsetPosition);
        }

        result = classifyStops(classifyPositions,spin->getBet(), lineidx);

        /**
         * If the bet is 0 we assume this is some special case and
         * use max-bet instead.  Prior bet validation prevents a spin from
         * happeneing with a 0 bet that isn't supposed to happen.
         */
        bet = spin->getBet() == 0 ? getBetCount() : spin->getBet();

        /**
         * Update the payout for this line.  Note we have to multiply the payout
         * by the denomination of the machine.
         */
        spin->setPayoutAmount(spinLine->lineid, 
                        getPayout(bet, result)->payoutAmount * m_info->creditValue,
                        bet == getBetCount() && result == getMaxPayout()->payoutTypeId,
                        result);
    }

	//Classify any scatter pays (implemented as a null function in the base)
	classifyScatters(spin);

    delete [] classifyPositions;

}


/**
 * The base class classify assumes the following:
 * 
 *   1. All reels have to match the same icon to win.
 *   2. The icon id and the payout id match.
 *   
 * 7/10/03 - UPDATE JOSH- BECAUSE OF FEAR OF RUNNING ON THIS GUT
 * I'M MAKING IT SET EVERYTHING TO LOSER, AND RAISING AN ERROR.
 */

uint8
SlotGuts::classifyStops(uint8 *stops, uint8 bet, uint8 line)
{

	uint8 result;
	result = 0;
	Log::msgError("IMPORTANT ERROR - CONTACT RTG: Attempting to play a game that the server exe doesnt know about - machine ID: %d", m_gutsid);



	/*
    result = getIcon(0, stops[0])->iconid;
    for ( uint8 i = 0; result != 0 && i < getReelCount(); i++ )
    {
        if ( getIcon(i, stops[i])->iconid != result )
        {
            result = 0;
        }
    }
	*/
    return result;
}


uint8
SlotGuts::getReelCount()
{
    return this->m_wheelHouse.size();
}


uint8 
SlotGuts::getIconCount(uint8 reelid)
{
    ISlotReel *reel = m_wheelHouse[reelid];
    return reel->size();
}

uint8 
SlotGuts::getBetCount()
{
    return m_payTable.size();
}


uint8 
SlotGuts::getPayoutCount(uint8 bet)
{
    ISlotPayColumn *payColumn = m_payTable[bet - 1];
    return payColumn->size();
}

        
const SlotGuts::SlotReel *
SlotGuts::getSlotReel(uint8 reelid)
{
    if ( reelid > getReelCount() )
    {
        /**
         * Shouldn't be possible to get a reelid greater than the number of reels
         * unless the servers are mismatched and we have a 5 reel accessing a 3 reel
         * guts.
         */
        ASSERT(0);
    }
    ISlotReel *reel = m_wheelHouse[reelid];
    return (SlotReel *) reel;
}


const SlotPayout *
SlotGuts::getPayout(uint8 bet, uint8 payouttypeid)
{
    if ( bet == 0 || bet > m_payTable.size() )
    {
        /**
         * Bet too small or too large.
         */
        ASSERT(0);
    }
    ISlotPayColumn *payColumn = m_payTable[bet - 1];

    if ( payouttypeid > payColumn->size() )
    {
        /**
         * Payout id is too large.
         */
        ASSERT(0);
    }

    const SlotPayout *payout = (*payColumn)[payouttypeid];

    return payout;
}

const SlotIcon *
SlotGuts::getIcon(uint8 reelid, uint8 position)
{
    if ( reelid > getReelCount() )
    {
        /**
         * reelid too large.
         */
        ASSERT(0);
    }
    ISlotReel *reel = m_wheelHouse[reelid];

    if ( position > reel->size() )
    {
        /**
         * position is off the end of the reel.
         */
        ASSERT(0);
    }
    const SlotIcon *icon = (*reel)[position];

    return icon;
}


void 
SlotGuts::addIcon(uint8 reelid, uint8 iconid, uint8 reelposition, int64 weight, const char *iconName)
{
    SlotIcon *icon;
    ISlotReel *reel;

    icon = new SlotIcon();

    icon->reelid = reelid;
    icon->iconid = iconid;
    icon->position = reelposition;
    icon->weight = weight;
    icon->iconName = new char[strlen(iconName) + 1];
    strcpy(icon->iconName, iconName);

    /**
     * Check to see if we need a new reel or not. 
     * 
     * Note that reelid is 0 based.
     */
    if ( reelid >= m_wheelHouse.size() )
    {
        reel = new ISlotReel();
        m_wheelHouse.push_back(reel);
    }
    else
    {
        reel = m_wheelHouse[reelid];
    }

    reel->push_back(icon);
}


void 
SlotGuts::clearWheelhouse()
{
    while ( m_wheelHouse.size() > 0 )
    {
        ISlotReel *reel = m_wheelHouse.back();
        while ( reel->size() > 0 )
        {
            SlotIcon *del = reel->back();
            delete [] del->iconName;
            delete del;
            reel->pop_back();
        }
        delete reel;
        m_wheelHouse.pop_back();
    }
}


const SlotGuts::SlotPayColumn * 
SlotGuts::getPayColumn(uint8 bet)
{
    ISlotPayColumn *column = m_payTable[bet - 1];
    return (SlotPayColumn *)column;
}


const SlotPayout *
SlotGuts::getMaxPayout()
{
    ISlotPayColumn *column = m_payTable.back();
    const SlotPayout *payout = column->back();

    return payout;
}

uint8
SlotGuts::getPaylineCount()
{
    SlotPaylineReel *paylinereel = m_paylines[0];

    return paylinereel->size();
}

uint8
SlotGuts::getPaylinePosition(uint8 paylineid, uint8 reelid)
{
    uint8 position = 255;

    SlotPaylinePosition *pPosition = getPayline( paylineid, reelid);

    if ( pPosition )
    {
        position = pPosition->wheelposition;
    }

    return position;
}

SlotPaylinePosition *
SlotGuts::getPayline(uint8 paylineid, uint8 reelid)
{
    SlotPaylinePosition *position = (SlotPaylinePosition *) 0;

    if ( reelid < m_paylines.size() )
    {
        SlotPaylineReel *paylinereel = m_paylines[reelid];

        if ( paylineid <= paylinereel->size() )
        {
            /**
             * Payline id is 1 based.
             */
            position = (*paylinereel)[paylineid - 1];
        }
        else
        {
            /**
             * paylineid is invalid.
             */
            ASSERT(0);
        }
    }
    else
    {
        /**
         * reelid is invalid.
         */
        ASSERT(0);
    }

    return position;
}



/**
 * Note that these are expected to come in order.
 */

void
SlotGuts::addPayline(uint8 paylineid, uint8 reelid, uint8 wheelposition)
{
    SlotPaylineReel *paylineReel = (SlotPaylineReel *) 0;
    SlotPaylinePosition *position;


    position = new SlotPaylinePosition();
    position->payline = paylineid;
    position->reelid = reelid;
    position->wheelposition = wheelposition;

    if ( reelid < m_paylines.size() )
    {
        paylineReel = m_paylines[reelid];
    }
    else
    {
        paylineReel = new SlotPaylineReel();
        m_paylines.push_back(paylineReel);

    }

    paylineReel->push_back(position);

}

void 
SlotGuts::addPayout(uint8 payoutTypeId, int8 betAmount, const char *payoutName, int32 payoutAmount)
{
    SlotPayout *payout;
    ISlotPayColumn *payColumn;


    payout = new SlotPayout();
    payout->payoutTypeId = payoutTypeId;
    payout->betAmount = betAmount;
    payout->payoutName = new char[strlen(payoutName) + 1];
    strcpy(payout->payoutName, payoutName);
    payout->payoutAmount = payoutAmount;

    /**
     * Check to see if we need a new pay column.
     * 
     * Note that bet amount is 1 based.
     */
    if ( (uint8)betAmount > m_payTable.size() )
    {
        payColumn = new ISlotPayColumn();
        m_payTable.push_back(payColumn);
    }
    else
    {
        payColumn = m_payTable[betAmount - 1];
    }

    payColumn->push_back(payout);
}



void
SlotGuts::clearPayTable()
{
    while ( m_payTable.size() > 0 )
    {
        ISlotPayColumn *column = m_payTable.back();

        while ( column->size() > 0 )
        {
            SlotPayout *del = column->back();
            delete [] del->payoutName;
            delete del;
            column->pop_back();
        }
        delete column;
        m_payTable.pop_back();
    }
}


        

        
Info *
SlotGuts::getInfo()
{
    return m_info;
}


bool8
SlotGuts::createBonusGame(int32 gamenum, uint8 linenumber, uint8 bonusgamenum, uint8 bonusgametype, uint8 playerchoice, int32 winresult)
{
	int32 result = SlotsSQL::ExecFailed;

    if ( tracker )
    {
		if(shouldSaveGameData())
		{
			tracker->lock();
			result = ((SlotsSQL*)tracker)->createBonusGame(this, gamenum, linenumber, bonusgamenum, bonusgametype, playerchoice, winresult);
			tracker->unlock();
		}
		else
		{
            // kharmon.  08-20-07.  #19498.  Need to update fun balance with bonus wins.
            if(winresult!=0)
            {
                int32 comp;
                tracker->lock();
                ((SlotsSQL*)tracker)->adjustBalance(ServerOpts::casinoId, getInfo()->pid, false, winresult, &getInfo()->balance, &comp);			
                tracker->unlock();
            }

			result = SlotsSQL::NoError;
		}
    }

	if(result == SlotsSQL::NoError)
	{
		if(m_spins.size() > 0)
		{
			SlotSpin* spin = m_spins[0];
			spin->addBonusGame(bonusgamenum, playerchoice, winresult);
		}
		else
		{
			ASSERT(0);
		}
	}

	saveSubGameData();

	return (bool8)(result == SlotsSQL::NoError);
}

bool8	
SlotGuts::saveGame()
{
	int32 result = SlotsSQL::ExecFailed;

	if (tracker && m_info)
	{
		tracker->lock();
		result = ((SlotsSQL*)tracker)->saveGame(ServerOpts::casinoId, this);
		tracker->unlock();
	}

	return (bool8)(result == SlotsSQL::NoError);
}


bool8
SlotGuts::loadSavedGame()
{
	int32 result = SlotsSQL::ExecFailed;

	int32 gutsid = -1;
	int32 gamenum = -1;
	uint8 bonusgamenum = 0;
	bool8 malfunctioned = false;
	bool8 completed = false;
	bool8 hasSavedGame = false;
	
	SlotSpin* restoredSpin = new SlotSpin();

	if (tracker && m_info)
	{
		//if we are not saving fun play game data, do not attempt to restore
		//saved fun play game data
		if(shouldSaveGameData())
		{
			tracker->lock();
			result = ((SlotsSQL*)tracker)->getSavedGame(this, &gutsid, &gamenum,&bonusgamenum,&malfunctioned,&completed);
			tracker->unlock();
		}
		else
		{
			result = SlotsSQL::NoError;
		}
	}
	if(result == SlotsSQL::NoError)
	{
		if(malfunctioned)
		{
			//MALFUNCTION -- NEED CODE
			/// @todo
			result = SlotsSQL::ExecFailed;
			Log::msgWarning("Malfunction is set in SavedGame: %d", gamenum);

            // FogBugz 753 - GChapman
            // Uncommented the line below.
			m_hardMalfunction = true;
		}
	}

	if(result == SlotsSQL::NoError && gamenum>0 && !completed && hasSavedGames())
	{
		//we have one that needs reading
		if(gutsid != getGutsId())
		{
			DEBUGMSG(("Saved game restored to a different guts."));
			///@todo need to figure out if that's okay or not.
			//It's okay for all of the first generation games (since no savedgame data is in guts)
		}
		
		//get the spin
		tracker->lock();
		result = ((SlotsSQL*)tracker)->loadGameDataIntoSpin(gamenum,this,restoredSpin);
		tracker->unlock();
	
		if(result == SlotsSQL::NoError)
		{
			//Set the bonus game type
			restoredSpin->setBonusGameType(this->m_subGame->getDataAt(0));
			//Load the spin in
			clearSpins();
			m_spins.push_back(restoredSpin);
			restoredSpin = 0;
			//set the gamenum
			hasSavedGame = true;
			//Set the bonus game type
		}
	} 

	delete restoredSpin;

	return (bool8)((result == SlotsSQL::NoError)&&hasSavedGames()&&hasSavedGame);

}

bool8	
SlotGuts::saveSpins()
{
	int32 result = SlotsSQL::ExecFailed;

	if (tracker && m_info)
	{
		tracker->lock();
		result = ((SlotsSQL*)tracker)->saveSpins(ServerOpts::casinoId, this);
		tracker->unlock();
	}

	return (bool8)(result == SlotsSQL::NoError);
}


bool8
SlotGuts::saveBonusGame()
{
    return true;
}


bool8	
SlotGuts::completeGame()
{
	//sanity check
	if (!isGameComplete())
	{
		ASSERT(0);
	}

	int32 result = SlotsSQL::ExecFailed;

	if (tracker && m_info)
	{
		if(shouldSaveGameData())
		{
			tracker->lock();
			result = ((SlotsSQL*)tracker)->completeGame(this);
			tracker->unlock();
		}
		else
		{
			result = true;
		}

		if ( result == true /*SlotsSQL::NoError*/ )
		{
			this->resetForNewGame();
		}
		else
		{
			/**
			 * Tracker call failed.
			 */
			ASSERT(0);
		}
	}

	return (bool8)(result == SlotsSQL::NoError);
}


bool8
SlotGuts::load(Info *info)
{
    bool8 rc = false;
    m_info = info;

    SlotsSQL *slotTracker = (SlotsSQL *) tracker;
    if ( tracker )
    {
		slotTracker->lock();

        if ( slotTracker->getGuts(this) == SlotsSQL::NoError )
        {
            rc = true;
        }

		slotTracker->unlock();
    }

    if ( rc )
    {
        rc = isValidGuts();
    }

    if ( rc )
    {
        allocateSubGameData();
        loadSubGameData();
    }
    else
    {
        /**
         * Guts is invalid.
         */
        DEBUGMSG(("Invalid guts detected for machid %d: gutsid: %d", m_machid, m_gutsid));
        ASSERT(0);
    }


    return rc;
}

bool8
SlotGuts::isValidGuts()
{
    /**
     * By default all guts are valid unless a list
     * is provided to restrict it.
     */
    bool8 rc = true;

    return rc;
}

		
bool8 
SlotGuts::isGameComplete()
{
    return m_gameComplete;
}

bool8
SlotGuts::setGameComplete(bool8 isComplete)
{
	return (m_gameComplete = isComplete);
}

       
bool8 
SlotGuts::isMalfunction()
{
    bool8 rc = m_hardMalfunction;

/**
 * For now, remove this so odds testing can be completed.
 */

#if 0


/**
 * Only do the max payout assertion if we are in
 * release mode.  This way we can at least get some
 * spin tests done.
 */


#ifndef DEBUG
    if ( !rc )
    {
        int32 won = 0;
        for ( uint8 i = 0; !rc && i < m_spins.size(); i++ )
        {
            SlotSpin *spin = m_spins[i];
            won += spin->getTotalPayoutAmount();
            rc = won > getMaxAggregate() || spin->getJackpot();
        }
    }
#endif

#endif

    return rc;
}

int32
SlotGuts::getMaxAggregate()
{
    return MAX_AGGREGATE * m_info->creditValue;
}


int32
SlotGuts::getRandomFromArray(int32* data, uint32 numElements)
{
	//This will be truly random if numElements is a power of two (it should be)
	//Although it won't matter too much if numElements is <<<< max_uint32

	uint32 randomValue = getrand();

	return data[randomValue % numElements];

}


uint8
SlotGuts::findPositionByWeight(int64 weight, uint8 reelid)
{
    int32 position = 0;


    if ( reelid > m_wheelHouse.size() )
    {
        /**
         * reelid is too large.
         */
        ASSERT(0);
    }

    ISlotReel *reel = m_wheelHouse[reelid];
    /**
     * Starting at the end, look for the first weight
     * that is greater than the target weight.
     */
    for ( int i = reel->size() - 1; i >= 0; i-- )
    {
        if ( weight > getIcon(reelid, i)->weight )
        {
            position = getIcon(reelid, i)->position;
            break;
        }
    }

    return position;
    
}

void 
SlotGuts::preSpinClassifyResults( SlotSpin* spin )
{
    // By default, this method does nothing but include debug spins, if needed.
    // But, if a slot machine needs to alter the values
    // on the reel prior to classifying the results
    // (as Super Diamond Mine does when it cascades the diamonds
    // on to other pay lines), then that can be done by overriding
    // this method in the derived class.

#ifdef DEBUGSPIN
	if(m_bDebugSpinsMode)
	{
		m_bForceRandomJackpot = false;
		processDebugStops( spin );
	}
#endif
}

void 
SlotGuts::postSpinClassifyResults( SlotSpin* spin )
{
    // By default, this method does nothing.
    // But, if a slot machine needs to alter the values
    // on the reel  after classifying the results
    // (as Super Diamond Mine does when it cascades the diamonds
    // on to other pay lines), then that can be done by overriding
    // this method in the derived class.
}

void
SlotGuts::setFastPlayEnabled( bool8 bFastPlayEnabled )
{
    m_bFastPlayEnabled = bFastPlayEnabled;
}


bool8
SlotGuts::getFastPlayEnabled() const
{
    return m_bFastPlayEnabled;
}

uint8 
SlotGuts::getDenominationCount() const
{
    return m_vecDenominations.size();
}

void
SlotGuts::clearDenominations()
{
    while ( m_vecDenominations.size() > 0 )
    {
        delete m_vecDenominations.back();
        m_vecDenominations.pop_back();
    }
}

SlotGameDenomination*
SlotGuts::getDenomination(uint8 nIndex)
{
    if ( nIndex >= 0 && nIndex < getDenominationCount() )
    {
        return m_vecDenominations[nIndex];
    }
    else
    {
        return NULL;
    }
}

void 
SlotGuts::addDenomination(uint16 machineSubId, uint16 creditValue, bool8 isDefault)
{
    SlotGameDenomination *pDenom = new SlotGameDenomination(machineSubId, creditValue, isDefault );
    m_vecDenominations.push_back(pDenom);
}

#ifdef DEBUGSPIN
void SlotGuts::processDebugStops( SlotSpin *spin )
{
	if (!m_debugReelStops.empty())
	{
        if ( m_nDebugSpinIndex >= m_debugReelStops.size() )
        {
            m_nDebugSpinIndex = 0;
        }
		
        if ( m_debugReelStops[m_nDebugSpinIndex][0] >= THIS_IS_A_FUNCTION )
        {
			// Feature function, find out which one and execute it
            int32 key = m_debugReelStops[m_nDebugSpinIndex][1];
			DebugStopFuncBase* func = m_mapDebugFunctions[key];
            (*func)(spin );
        }
        else
        {            
			// No function, just copy stop values 
			ReelDebugStops rStops = m_debugReelStops[m_nDebugSpinIndex];
			spin->setStops(getReelCount(), &rStops[0]);              
        }

		m_nDebugSpinIndex++;
	}
}

void SlotGuts::initDebugFunctionMap()
{
	addDebugStopFunction( EVT_DEBUG_FUNCTION_LOSER_SPIN,
		new DebugStopFunc<SlotGuts>(this, &SlotGuts::DebugStops_Loser) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_RANDOM_SPIN,
		new DebugStopFunc<SlotGuts>(this, &SlotGuts::DebugStops_RandomSpin) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_RANDOM_JACKPOT,
		new DebugStopFunc<SlotGuts>(this, &SlotGuts::DebugStops_RandomJackpot) );
}

void SlotGuts::addDebugStopFunction( int32 key, DebugStopFuncBase* func )
{
    m_mapDebugFunctions.insert( make_pair( key,func ) );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 SlotGuts::Debug_SetLoserStops( uint8* pStops )
///
/// @param   pStops		The stops to set.
///
/// This debug stop function gives a set of losing stops.
/// Derived classed should override if all zeros does not create a loser.
////////////////////////////////////////////////////////////////////////////
void SlotGuts::DebugStops_Loser( SlotSpin *spin )
{
	uint8* pStops = const_cast<uint8*>(spin->getStops());

	Debug_SetLoserStops( pStops );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 SlotGuts::Debug_SetLoserStops( uint8* pStops )
///
/// @param   pStops		The stops to set.
///
/// This debug stop function gives derived class a chance to define a losing
/// stop.
/// Derived classed should override if all zeros does not create a loser.
////////////////////////////////////////////////////////////////////////////
void SlotGuts::Debug_SetLoserStops( uint8* pStops )
{
	// We set 1 for all stops. 
	// If for some game this is not a loser spin, this method has to be overriden in corresponding class
	memset( pStops, 1, getReelCount() * sizeof( uint8 ) );	
}

void SlotGuts::DebugStops_RandomSpin( SlotSpin *spin)
{
	return;
}

void SlotGuts::DebugStops_RandomJackpot( SlotSpin *spin)
{
	m_bForceRandomJackpot = true;
}
#endif



